#include "DiskXFile.h"
#include "xtprobeImageDiskXFile.h"
#include "ImageRgb.h"
#include "ipSegmentDiagGMM.h"
#include "ipHistoEqual.h"
#include "ipSmoothGaussian3.h"
#include "ipErode.h"
#include "ipDilate.h"
#include "ipClose.h"
#include "ipCanny.h"
#include "tp6.h"
#include <vector>



Process::Process(char *name)
{
	image_in = new ImageRgb();
  	image_in->setBOption("verbose",false);
  
  	ImageDiskXFile *image_file = new xtprobeImageDiskXFile(name,"r");
	file_name = (char*) malloc (sizeof(char)*longueur(name)+1);
	
	strcpy(file_name,name);
	image_in->loadImageXFile(image_file);

  	//on libere l'espace occupe par image_file
  	
	this->number=0;	
	//construction de l'image des niveaux de gris
	this->imageGray = new ImageGray(image_in->width,image_in->height);
	for(int x=0; x<image_in->height; x++)
		for(int y=0; y<image_in->width; y++) {
			int valeur = (int)round((double)(image_in->get(x,y)[0] + image_in->get(x,y)[1] + image_in->get(x,y)[2])/3);
			
			for (int i=0; i<3; i++) {
				if( valeur > 94 && valeur < 100 )
					this->imageGray->get(x,y)[i] = valeur + 120;
				else
					this->imageGray->get(x,y)[i] = valeur;
			}	
		}
	
	//sauvegarde de l'image grise correspondante 
	this->imageGray->updateDataFromPixmap();
	char gray[] = {'_','G','r','a','y','.','p','m','g','\0'};
	this->imageGray->save(newName(file_name,gray));
	
	
	delete image_file; 
			
	
}




void Process::segmentation(ImageRgb*image)
{
	int n_gaussians = 2;
 	//bool octon;
  	//bool gray = false;
 	bool xy =false;
 	//bool norm = false; //normalise les pixels
 	bool verbose = false;


	//amelioration de l'image

	
	//fin amelioration
	
	ipCore *imachine = NULL;
	//imageGray
	//if(gray) imachine = new ipSegmentDiagGMM(image_in->width, image_in->height, "gray", n_gaussians, xy);

	imachine = new ipSegmentDiagGMM(image_in->width, image_in->height, "gray", n_gaussians, xy);
 	//else 
	//imachine = new ipSegmentDiagGMM(image_in->width, image_in->height, "rgb", n_gaussians, xy, norm);
 	imachine->setBOption("verbose", verbose);
 	//imachine->process(image_in);
	imachine->process(imageGray);
	
	
	print(" Segmantation done.  \n");
 	   
  	{
 		binaryImage = new ImageRgb(image_in->width, image_in->height);

		Color c1 =black;
		Color c2 = white;
		int labeltest = (int) imachine->seq_out->frames[0][0*image_in->width+0];
		if(labeltest != 1)
		{
			c1 = white;
			c2 = black;
		} 		
		
  		for(int y = 0; y < image_in->height; y++)
  			for(int x = 0; x < image_in->width; x++)
 			{
		   		int label_ = (int) imachine->seq_out->frames[0][y*image_in->width+x];
  				Color c;
				if(label_ == 1) c = c1;
  				else if(label_ == 2) c = c2;
   				binaryImage->drawpixel(x, y, c);
				if( label_ != 1 && label_ !=2) 
					binaryImage->drawpixel(x, y,white);
  			}
		
  		char bin[] = {'_','b','i','n','a','i','r','e','.','p','p','m','\0'};
  		
		binaryImage->updateDataFromPixmap();
  		binaryImage->save(newName(file_name,bin));
 		//delete blobimage;
 	}
 	delete imachine;

}

//imageBin est l'image binaire
void Process::dilatation(ImageRgb *imageBin)
{	
	this->imageDilate = new ImageRgb(image_in->width,image_in->height);
	
	//previous permet de conserver la version precedente de l'image dilatee
	ImageRgb *previous = new ImageRgb(image_in->width,image_in->height);	
	//Le processus de segmentation est appliquee sur l'image binaire obtenue
	//apres la phase de segmentation 
	for(int x=0; x<binaryImage->height; x++)
		for(int y=0; y<binaryImage->width; y++) {
			for (int i=0; i<3; i++) {
				this->imageDilate->get(x, y)[i] = binaryImage->get(x, y)[i];	
				previous->get(x, y)[i] = binaryImage->get(x, y)[i]; 			
			}
	}	
	for (int i=0; i<9; i++) {//le processus de dilatation est realise plusieurs fois afin de fermer le maximun de trous
		for(int x=0; x<binaryImage->height; x++)
		for(int y=0; y<binaryImage->width; y++) {
			for (int i=0; i<3; i++) {
				// = binaryImage->get(x, y)[i];	
				previous->get(x, y)[i] = this->imageDilate->get(x, y)[i]; 			
			}
		}		
		for(int x=0; x<image_in->height; x++)
			for(int y=0; y<image_in->width; y++) {
				
				if ((x == 0) || (x == image_in->height) || (y == 0) || (y == image_in->width)) {
					for (int j=0; j<3; j++) {
						this->imageDilate->get(x, y)[j] = 0;
					}				
				} else if ((previous->get(x-1, y)[0] == 255) || (previous->get(x+1, y)[0] == 255) || (previous->get(x, y-1)[0] == 255) || (previous->get(x, y+1)[0] == 255)) {
					for (int j=0; j<3; j++) {
						this->imageDilate->get(x, y)[j] = 255;
					}					
				} else {
					for (int j=0; j<3; j++) {
						this->imageDilate->get(x, y)[j] = 0;
					}					
				}
			}
	}
	
	this->imageDilate->updateDataFromPixmap();
	char dil[] = {'_','d','i','l','a','t','e','e','.','p','p','m','\0'};
	this->imageDilate->save(newName(file_name,dil));
	delete previous;
}



//imageErode est l'image resultat
//cet argument n'est vraiment pas utilise dans le 
//bout de code qui suit 
void Process::erode(ImageRgb *imageErode)
{
	//Dans cette methode, nous faisons directement le fermeture car nous partons 
	//de l'image dilatee on a donc:
	// Dilatation + Erosion --> Fermeture. 
	
	this->imageErode = new ImageRgb(image_in->width,image_in->height);
	
	//previous permet de conserver la version precedende 
	//de l'image erodee comme dans la procedure dilatation
	ImageRgb *previous = new ImageRgb(image_in->width,image_in->height);

	//recopie de l'image dilatee pour l'erosion
	for(int x=0; x<image_in->height; x++)
		for(int y=0; y<image_in->width; y++) {
			for (int i=0; i<5; i++) {
				this->imageErode->get(x, y)[i] = this->imageDilate->get(x, y)[i]; 
				previous->get(x, y)[i] = this->imageDilate->get(x, y)[i];  
			}			
		}		
	for (int i=0; i<5; i++) {//le processus d'erosion est realiser 5 fois pour toutes les images
				//en fonction du resultat que l'on souhaite obtenir, on peut modifier 
				//cette valeur.
		for(int x=0; x<binaryImage->height; x++)
		for(int y=0; y<binaryImage->width; y++) {
			for (int i=0; i<3; i++) {
				// = binaryImage->get(x, y)[i];	
				previous->get(x, y)[i] = this->imageErode->get(x, y)[i]; 			
			}
		}				
		for(int x=0; x<image_in->height; x++)
			for(int y=0; y<image_in->width; y++) {
				
				if ((x == 0) || (x == image_in->height) || (y == 0) || (y == image_in->width)) {
					for (int j=0; j<3; j++) {
						this->imageErode->get(x, y)[j] = 0;
					}			
				} else if ((previous->get(x-1, y)[0] == 0) || (previous->get(x+1, y)[0] == 0) || (previous->get(x, y-1)[0] == 0) || (previous->get(x, y+1)[0] == 0)) {
					for (int j=0; j<3; j++) {
						this->imageErode->get(x, y)[j] = 0;
					}					
				} else {
					for (int j=0; j<3; j++) {
						this->imageErode->get(x, y)[j] = previous->get(x, y)[j];;
					}	
				}				
			}
	}
	this->imageErode->updateDataFromPixmap();
	char erod[] = {'_','e','r','o','d','e','e','.','p','p','m','\0'};
	this->imageErode->save(newName(file_name,erod));
	delete previous;	
}


void Process::init()
{
	this->label = (int**)malloc (sizeof (int*)*image_in->height);
	//on initialise toute les regions zero 
	//la valeur zero va nous servir de drapeau lors du parcours de la matrice label
	for (int x=0; x<image_in->height; x++) {
		label[x] = (int*)malloc (sizeof (int)*image_in->width);
		for (int y=0; y<image_in->width; y++) {
			this->label[x][y] = 0;
		}
	}
	//la boucle ci-dessous permet d'affecter une numero d'etiquette a chaque pixel de l'image
	//parcours du haut vers le bas et de gauche a droite
	for(int i=0; i<image_in->height; i++)
		for(int j=0; j<image_in->width; j++) {
			if ((this->imageErode->get(i, j)[0]) != 0) { 
				if ((this->label[i-1][j-1] == 0) && (this->label[i-1][j] == 0) && (this->label[i-1][j+1] == 0) && (this->label[i][j-1] == 0)) {
					this->number++;
					label[i][j] = this->number;					
				} else if (this->label[i-1][j] != 0) {
					this->label[i][j] = this->label[i-1][j];
				} else if (this->label[i][j-1] != 0) {
					this->label[i][j] = this->label[i][j-1];
				} else if (this->label[i-1][j+1] != 0) {
					this->label[i][j] = this->label[i-1][j+1];
				} else if (this->label[i-1][j-1] != 0) {
					this->label[i][j] = this->label[i-1][j-1];
				} 				
			}
		}
}





/*
* La methode ci-dessous etiquette chaque region (objet) de l'image 
*par un numero 
* Il est en majeur partie inspire des codes implementes dans Torch3vision 
*/
void Process::imageLabelling()
{	

	init();
	
	//la variable perfom permet de controler la boucle et s'assurer que plus aucun pixel ne change sa valeur.
	bool perfom = true;
	//pour certains objets par exemple des objets avec des contours en spirale, la boucle peut duree plus de 30 minutes 
	//pour des raisons de perfomances, nous allons limiter le nombre d'iteration a 30
	//bien sure que ce choix est subjectif on peut faire mieux   	
	int nbreitteration = 0; 
	while (true) {
		perfom = true; 	
		nbreitteration+=1;
		//parcours du haut vers le bas et de gauche a droite 	
		for(int x=image_in->height-1; x>=0; x--)
			for(int y=image_in->width-1; y>=0; y--)
			 {
				if (this->imageErode->get(x, y)[0] != 0)
				 {	// Considérer seulement les pixels correspondants aux objets
					if (label[x][y+1] != 0) 
					{
						if (label[x][y] != label[x][y+1]) 
						{
							label[x][y] = label[x][y+1];
							perfom = false;
						}
					} else 
					if (label[x+1][y] != 0) 
					{
						if (label[x][y] != label[x+1][y])
						{
							label[x][y] = label[x+1][y];
							perfom = false;
						}						
					} else 
					if (label[x+1][y-1] != 0)
					{
						if (label[x][y] != label[x+1][y-1]) 
						{
							label[x][y] = label[x+1][y-1];
							perfom = false;
						}						
					} else 
					if (label[x+1][y+1] != 0)
					{
						if (label[x][y] != label[x+1][y+1])
						{
							label[x][y] = label[x+1][y+1];
							perfom = false;
						}						
					}
				}		
			}
			//parcours du bas vers le haut de droite a gauche
			for(int x=0; x<image_in->height; x++)
				for(int y=0; y<image_in->width; y++)
				{
					if (this->imageErode->get(x, y)[0] != 0)
					{ 
						if (label[x-1][y] != 0) 
						{
							if (label[x][y] != label[x-1][y])
							{
								label[x][y] = label[x-1][y];
								perfom = false;
							}						
						} else
						if (label[x][y-1] != 0)
						{
							if (label[x][y] != label[x][y-1])
							{
								label[x][y] = label[x][y-1];
								perfom = false;
							}						
						} else
						if (label[x-1][y+1] != 0)
						{
							if (label[x][y] != label[x-1][y+1])
							{
								label[x][y] = label[x-1][y+1];
								perfom = false;
							}						
						} else
						if (label[x-1][y-1] != 0)
						{
							if (label[x][y] != label[x-1][y-1])
							{
								label[x][y] = label[x-1][y-1];
								perfom = false;
							}
						}				
					}
				}
		//on considere aussi les distances de deux pour etre plus rapide
		perfom = topDown2(perfom);
		perfom = downtop2(perfom);
		//on perfome la boucle si le nombre d'itteration atteind 50
		//ce choix est fait fait pour ne pas attendre trop longtemps 
		//indispensable pour certaines images. L'objet 1 par exemple 
		if (perfom || nbreitteration == 50) {
			break;
		}
	}
}

bool Process::topDown2(bool perfom)
{
	for(int x=image_in->height-1; x>=0; x--)
		for(int y=image_in->width-1; y>=0; y--) {
			if (this->imageErode->get(x, y)[0] != 0) {	
				if (label[x+2][y-2] != 0) {
					if (label[x][y] != label[x+2][y-2]) {
						label[x][y] = label[x+2][y-2];
						perfom = false;
					}						
				} else if (label[x+2][y-1] != 0) {
					if (label[x][y] != label[x+2][y-1]) {
						label[x][y] = label[x+2][y-1];
						perfom = false;
					}						
				} else if (label[x+2][y] != 0) {
					if (label[x][y] != label[x+2][y]) {
						label[x][y] = label[x+2][y];
						perfom = false;
					}						
				} else if (label[x][y+2] != 0) {
					if (label[x][y] != label[x][y+2]) {
						label[x][y] = label[x][y+2];
						perfom = false;
					}
				} else if (label[x+2][y+2] != 0) {
					if (label[x][y] != label[x+2][y+2]) {
						label[x][y] = label[x+2][y+2];
						perfom = false;
					}						
				} else if (label[x+2][y+1] != 0) {
					if (label[x][y] != label[x+2][y+1]) {
						label[x][y] = label[x+2][y+1];
						perfom = false;
					}						
				} else if (label[x+1][y+2] != 0) {
					if (label[x][y] != label[x+1][y+2]) {
						label[x][y] = label[x+1][y+2];
						perfom = false;
					}						
				}
			}		
		}
	return perfom;
}

bool Process::downtop2(bool perfom)
{
	for(int x=0; x<image_in->height; x++)
		for(int y=0; y<image_in->width; y++) {
			if (this->imageErode->get(x, y)[0] != 0) {	
				if (label[x-2][y+2] != 0) {
					if (label[x][y] != label[x-2][y+2]) {
						label[x][y] = label[x-2][y+2];
						perfom = false;
							}						
						} else if (label[x-2][y+1] != 0) {
						if (label[x][y] != label[x-2][y+1]) {
							label[x][y] = label[x-2][y+1];
							perfom = false;
						}						
					} else if (label[x-2][y] != 0) {
						if (label[x][y] != label[x-2][y]) {
							label[x][y] = label[x-2][y];
							perfom = false;
						}						
					} else if (label[x][y-2] != 0) {
						if (label[x][y] != label[x][y-2]) {
							label[x][y] = label[x][y-2];
							perfom = false;
							}						
						} else if (label[x-2][y-1] != 0) {
						if (label[x][y] != label[x-2][y-1]) {
							label[x][y] = label[x-2][y-1];
							perfom = false;
						}						
					} else if (label[x-2][y-2] != 0) {
						if (label[x][y] != label[x-2][y-2]) {
							label[x][y] = label[x-2][y-2];
							perfom = false;
						}
					} else if (label[x-1][y-2] != 0) {
						if (label[x][y] != label[x-1][y-2]) {
							label[x][y] = label[x-1][y-2];
							perfom = false;
						}
					}				
				}
			}
	return perfom;

}

//le sous code ci-dessous est inspiree de Torch3 vision
void Process::buildImage()
{
	ImageRgb *image_out = new ImageRgb(image_in->width,image_in->height);
	for(int x=0; x<image_in->height; x++)
		for(int y=0; y<image_in->width; y++) {
			if (this->imageErode->get(x, y)[0] == 0) {
				for (int j=0; j<3; j++) {
					image_out->get(x, y)[j] = 0;//correspond au fond de l'image
				}				
			} else {
				for (int j=0; j<3; j++) {
					image_out->get(x, y)[j] = image_in->get(x,y)[j];	
				}				
			}
		}	
	image_out->updateDataFromPixmap();
	char result[] = {'_','r','e','s','u','l','t','a','t','.','p','p','m','\0'};
	image_out->save(newName(file_name,result));		
}

//dessine chaque image individuellement. 
void Process::buildIndividual()
{	
	int toBeSaved[this->number];	
	Image *image_out[this->number];
	Image *image_out_masque[this->number];
	for (int j=0; j<this->number; j++) {
		toBeSaved[j] = 0;
		image_out_masque[j] = new ImageRgb(image_in->width,image_in->height);
		image_out[j] = new ImageRgb(image_in->width,image_in->height);
		for(int x=0; x<image_in->height; x++)
			for(int y=0; y<image_in->width; y++) {
				for (int i=0; i<3; i++) {
					image_out[j]->get(x,y)[i] = 0;
					image_out_masque[j]->get(x,y)[i] = 0;
				}				
				if (label[x][y]==j+1) {	
					toBeSaved[j] = 1;
					for (int i=0; i<3; i++) {
						//restitue la valeur originale du pixel correspondant
						image_out[j]->get(x,y)[i] = image_in->get(x, y)[i]; 
						image_out_masque[j]->get(x,y)[i] = 255;
					}
				}
			}
	}
	//sauvegarde de chaque objet individuel
	char name[50];
	char masque[60];
	char result[] = {'_','e','l','e','m','e','n','t','_','\0'};
	char result_mask[] = {'_','m','a','s','q','u','e','_','e','l','e','m','e','n','t','_','\0'};
	char *objet = newName(file_name,result);
	char *objet2 = newName(file_name,result_mask);
	for (int l=0; l<this->number; l++) {
		if (toBeSaved[l] == 1) {		
			sprintf(name,"%s%d%s",objet,l+1,".ppm"); 
			sprintf(masque,"%s%d%s",objet2,l+1,".ppm"); 
			if(checkimage(image_out[l])){
			image_out[l]->save(name);
			image_out_masque[l]->save(masque);
			}
		}
	}
}


bool Process::checkimage(Image *img)
{
	bool find = false;
	for(int x=0; x<img->height; x++)
		for(int y=0; y<img->width; y++) {
			int valeur = (int)round((double)(img->get(x,y)[0] + img->get(x,y)[1] + img->get(x,y)[2])/3);
			
			if(valeur != 0 && valeur < 255 )
			{
				find = true;
				break;
			}	
		}
	return find;
		

}


char* Process::newName(char*oldname,char*additionName)
{
	//on soustrait 4 pour fairepartir les informations du type de l'image
	int l = longueur(oldname) - 4;
	int size = l + longueur(additionName) + 1; 

	char* nouveau = (char*)malloc(sizeof(char)*(l+longueur(additionName) + 1));
	int i = 0;
	for(i = 0;i<l;i++)
		nouveau[i] = oldname[i]; 
	//printf("\t%s\n",nouveau);
	for(int t=0,k=i; k< size;t++,k++)
		nouveau[k]=additionName[t];

	return (nouveau);
	
}

int Process::longueur(char* chaine)
{
	int k =0;
	for(k=0;chaine[k]!='\0';k++);
	
	return(k);	
}

void Process::freeSpace()
{
	for(int i =0;i<image_in->height;i++)
		delete []label[i];
	
	delete []label;

	label = 0;
	
	delete image_in;
	delete binaryImage;
	delete imageDilate;
	delete imageErode;
	delete imageGray;
	
}
















